﻿
#include "nonstd/jthread.hpp"
#include "nonstd/string.hpp"
#include "nonstd/span.hpp"
#include "nonstd/scope.hpp"
#include "nonstd/ring.hpp"

#include "nod/nod.hpp"

#include "uuid.h"
using namespace uuids;

namespace
{
    // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0205r0.html
    template <typename EngineT, std::size_t StateSize = EngineT::state_size>
    void seed_rng(EngineT& engine)
    {
        using engine_type = typename EngineT::result_type;
        using device_type = std::random_device::result_type;
        using seedseq_type = std::seed_seq::result_type;
        constexpr auto bytes_needed = StateSize * sizeof(engine_type);
        constexpr auto numbers_needed = (sizeof(device_type) < sizeof(seedseq_type))
            ? (bytes_needed / sizeof(device_type))
            : (bytes_needed / sizeof(seedseq_type));
        std::array<device_type, numbers_needed> numbers{};
        std::random_device rnddev{};
        std::generate(std::begin(numbers), std::end(numbers), std::ref(rnddev));
        std::seed_seq seedseq(std::cbegin(numbers), std::cend(numbers));
        engine.seed(seedseq);
    }
}

#define FMT_HEADER_ONLY
#define FMT_UNICODE 0
#include <fmt/core.h>
#include <fmt/ranges.h>

void a(nonstd::span<uint8_t> aa)
{
    int idx = 0;
    for (int i = 0; i < aa.size(); i++)
    {
        fmt::print("  {} - {}\n", idx++, aa[i]);
    }
}

#define AUTO_NEW_ARR(type, name, size)                   \
    type* name         = new type[size];                 \
    auto  name##_guard = nonstd::make_scope_exit([&]() { \
        delete[] name;                                  \
    });
#define AUTO_NEW(type, name)                             \
    type* name         = new type;                       \
    auto  name##_guard = nonstd::make_scope_exit([&]() { \
        delete name;                                    \
    });
#define AUTO_MALLOC(type, name, size)                        \
    type* name         = (type*)malloc(size * sizeof(type)); \
    auto  name##_guard = nonstd::make_scope_exit([&]() {     \
        free(name);                                         \
    });


void print_exit_status(const char* name, bool exit_status, bool did_throw) {
    fmt::print("{}:\n", name);
    fmt::print("  Throwed exception  {}\n", (did_throw ? "yes" : "no"));
    fmt::print("  Exit status        {}\n\n",(exit_status ? "finished" : "pending"));
}

void maybe_throw() {
	//if (std::rand() >= RAND_MAX / 2)
	//	throw std::exception{};
}

int main()
{
    auto engine = uuids::uuid_random_generator::engine_type{};
    seed_rng(engine);

    uuids::uuid_random_generator gen{ engine };
    auto id = gen();
    fmt::println("{}", uuids::to_string(id));
    uuid guid = uuids::uuid_random_generator{ engine }();

    fmt::println("{}", uuids::to_string(guid));

    // Create a signal which accepts slots with no arguments and void return value.
    nod::signal<void()> signal;
    // Connect a lambda slot that writes "Hello, World!" to stdout
    nod::connection connection = signal.connect([]() {
        fmt::println("Hello, World!");
        });
    // Call the slots
    signal();
    connection.disconnect();

    {
        // Let's save the connection in a scoped_connection
        nod::scoped_connection connection =
            signal.connect([]() {
            fmt::println("This message should only be emitted once!");
                });
        // If we trigger the signal, the slot will be called
        signal();
    } // Our scoped connection is destructed, and disconnects the slot
    signal();

	std::array<uint8_t, 2048>          arr;
	nonstd::ring_span<uint8_t>  rb{ arr.begin(), arr.end() };

    uint8_t buffer[] = { 0, 1 , 2 };
    std::copy(buffer, buffer + sizeof(buffer), std::back_inserter(rb));

	for (size_t i = 0; i < sizeof(buffer); i++)
	{
        fmt::print("rb {}\n", rb.pop_front());
	}

	bool exit_status{ false }, did_throw{ false };

	// Manual handling at "end of scope"
	try {
		maybe_throw();
		exit_status = true;
	}
	catch (...) { did_throw = true; }
	print_exit_status("Manual handling", exit_status, did_throw);

	// Using scope_exit: runs on scope exit (success or exception)
	exit_status = did_throw = false;
	try {
		auto guard = nonstd::make_scope_exit( [&] { exit_status = true; } );
		maybe_throw();
	}
	catch (...) { did_throw = true; }
	print_exit_status("scope_exit", exit_status, did_throw);

	// Using scope_fail: runs only if an exception occurs
	exit_status = did_throw = false;
	try {
		auto guard = nonstd::make_scope_fail([&] { exit_status = true; });
		maybe_throw();
	}
	catch (...) { did_throw = true; }
	print_exit_status("scope_fail", exit_status, did_throw);

	// Using scope_success: runs only if no exception occurs
	exit_status = did_throw = false;
	try {
		auto guard = nonstd::make_scope_success([&] { exit_status = true; });
		maybe_throw();
	}
	catch (...) { did_throw = true; }
	print_exit_status("scope_success", exit_status, did_throw);

    std::string          ssss("Hello World!");

    nonstd::jthread th([&]() {
        int idx = 1;

        {
            AUTO_NEW_ARR(int, a, 100);
            AUTO_MALLOC(int, b, 100);
            AUTO_NEW(std::string, c);

            a[0] = 0x11223344;
            b[0] = 0x11223344;
            *c   = "0xAA";
        }

        for (auto sv : nonstd::string::split(ssss, " "))
        {
            std::string s = sv.to_string();
            if (s.size() != 0)
            {
                fmt::print("  {} - {}\n", idx++, nonstd::string::trimmed(s));
            }
        }

        fmt::print("--\n");

        {
            std::vector<uint8_t> arr{ 1, 2, 3, 4 };
            a(arr);
        }
    });

    fmt::print("{}\n", ssss);
}
